package com.dhy.socket.request.core;

import com.dhy.socket.request.util.ControllerHolder;
import com.dhy.socket.request.util.ParamHolder;
import com.dhy.socket.request.util.RequestKey;
import com.dhy.socket.request.util.Scanner;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import static io.netty.handler.codec.http.HttpHeaderNames.*;

/**
 * @author 大忽悠
 * @create 2022/6/20 17:29
 */
@Slf4j
public class RequestDispatcher {
    /**
     * 请求映射集合
     */
    private static final Map<RequestKey, ControllerHolder> REQUEST_MAPPINGS=new ConcurrentHashMap<>(16);
    /**
     * 错误处理器
     */
    private static final ErrorController ERROR_CONTROLLER=new ErrorController();

    private static final ObjectMapper objectMapper=new ObjectMapper();

    static {
        init();
    }

    /**
     * 初始化方法---包扫描,寻找方法和请求的映射关系
     */
    public static void init(){
        Scanner.scanPackage("com.dhy.socket.controller",new File("target/classes/com/dhy/socket/controller"),REQUEST_MAPPINGS);
    }

    /**
     * 寻找当前请求对应的处理器
     */
    public static void handle(RequestKey requestKey){
        ControllerHolder holder = REQUEST_MAPPINGS.get(requestKey);
        if(holder==null){
            log.error("not found mapping for: {}",requestKey);
            handleMappingNotFound(requestKey);
            return;
        }
        //存在映射关系,调用controller相关方法,完成请求处理
        Object res = invokeController(holder, requestKey.getRequest());
        //处理controller方法返回值
        handleControllerRes(res,requestKey.getResponse());
    }

    /**
     * 返回值都会被JSON序列化
     */
    private static void handleControllerRes(Object res, DefaultFullHttpResponse response) {
        if(res==null){
            response.headers().setInt(CONTENT_LENGTH, 0);
            return;
        }
        try {
            String resStr = objectMapper.writeValueAsString(res);
            byte[] resBytes = resStr.getBytes(StandardCharsets.UTF_8);
            //如果不加响应头标注响应数据长度，浏览器会一直转圈圈等待客户端将剩余数据传输过来
            response.headers().setInt(CONTENT_LENGTH, resBytes.length);
            //添加跨域请求头
            response.headers().set(ACCESS_CONTROL_ALLOW_ORIGIN,"*");
            response.headers().set(ACCESS_CONTROL_ALLOW_HEADERS,"*");
            response.headers().set(ACCESS_CONTROL_ALLOW_METHODS,"*");
            response.headers().set(ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
            response.headers().set(ACCESS_CONTROL_MAX_AGE, "3600");
            response.headers().set("Content-Type", "text/html; charset=utf-8");
            response.content().writeBytes(resBytes);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
    }

    private static Object invokeController(ControllerHolder holder,DhyHttpRequest request) {
        Method targetMethod = holder.getTargetMethod();
        Object targetObj = holder.getControllerObj();
        //参数数组
        List<ParamHolder> paramHolders = holder.getParamHolderList();
        Object[] params = new Object[paramHolders.size()];
        try {
            for (int i = 0; i < paramHolders.size(); i++) {
                String paramName = paramHolders.get(i).getParamName();
                Class<?> paramClass = paramHolders.get(i).getParamClass();
                if(paramClass.equals(Integer.class)){
                    params[i]=request.getParam(paramName)==null?null:Integer.valueOf(request.getParam(paramName));
                }else {
                    params[i]=request.getParam(paramName);
                }
            }
            return targetMethod.invoke(targetObj, params);
        } catch (IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }

    private static void handleMappingNotFound(RequestKey requestKey) {
         ERROR_CONTROLLER.handleError(requestKey.getRequest(),requestKey.getResponse());
    }
}
